﻿using Fantom.Expression;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Media.Animation;
using An_Im.Expression.Model;
using System.Windows.Controls;
using System.Windows.Documents;
using PPT = Microsoft.Office.Interop.PowerPoint;
using System.ComponentModel;
using Microsoft.Office.Interop.PowerPoint;

namespace An_Im.Expression
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class KeyPointFrameEditorControl : UserControl
    {
        public MainEditModel EditModel => mainEditModel;
        private MainEditModel mainEditModel;
        private PPT.AnimationPoints _pptPoints;
        public KeyPointFrameEditorControl()
        {
            InitializeComponent();
            

            //var list = new List<KeyPointInformation>()
            //{
            //    new KeyPointInformation(0,"0","((1-floor($)%2)*($-floor($))+(floor($)%2)*(1+floor($)-$))"),
            //    new KeyPointInformation(0.5,"5","0.5+9/16*1/2*cos(sin($*2*pi)*pi/2+pi/2)"),
            //    new KeyPointInformation(1,"10", "0")
            //};

            DataContext = mainEditModel;
            this.SizeChanged += MainWindow_SizeChanged;

        }

        /// <summary>
        /// 导入配置
        /// </summary>
        /// <param name="msoAnimProperty"></param>
        /// <param name="keyPoints"></param>
        public void Load(PPT.MsoAnimProperty msoAnimProperty, PPT.AnimationPoints pptPoints, IEnumerable<KeyPointInformation> keyPoints, PPT.Shape shape)
        {

            _pptPoints = pptPoints;
            var canvasWidth = 980d;
            var canvasHeight = 540d;

            SystemConstCatalog.DefaultSystemConsts["ppt_x"] = (shape.Left + shape.Width / 2) / canvasWidth;
            SystemConstCatalog.DefaultSystemConsts["ppt_y"] = (shape.Top + shape.Height / 2) / canvasHeight;
            SystemConstCatalog.DefaultSystemConsts["ppt_w"] = shape.Width / canvasWidth;
            SystemConstCatalog.DefaultSystemConsts["ppt_h"] = shape.Height / canvasHeight;
            SystemConstCatalog.DefaultSystemConsts["ppt_r"] = shape.Rotation;

            mainEditModel = new MainEditModel(msoAnimProperty,keyPoints);

            // keyPointListBox.Items.Clear();

            keyPointListBox.ItemsSource = mainEditModel.KeyPointList;
            keyPointListBox.SelectedIndex = 0;
            mainEditModel.SelectedIndex = 0;
            ResetCanvas();

            DataContext = mainEditModel;

        }

        public void Save(PPT.AnimationPoints points)
        {
            var keypoints = mainEditModel.KeyPointList;
            if (points.Count < keypoints.Count)
            {
                for (int i = points.Count; i < keypoints.Count; i++)
                {
                    points.Add();
                }
            }
            else if (points.Count > keypoints.Count)
            {
                var count = points.Count;

                for (int i = keypoints.Count; i < count; i++)
                {
                    var point = points._Index(1) as PPT.AnimationPoint;
                    point.Delete();
                }
            }


            for (int i = 0; i < keypoints.Count; i++)
            {
                var point = points._Index(i + 1) as PPT.AnimationPoint;
                var point2 = keypoints[i];
                point.Value = point2.Value;
                point.Formula = point2.Formula;
                point.Time = (float)point2.Time;
            }

        }

        private void ResetCanvas()
        {
            var pptx = SystemConstCatalog.DefaultSystemConsts["ppt_x"];
            var ppty = SystemConstCatalog.DefaultSystemConsts["ppt_y"];
            var pptw = SystemConstCatalog.DefaultSystemConsts["ppt_w"];
            var ppth = SystemConstCatalog.DefaultSystemConsts["ppt_h"];
            var pptr = SystemConstCatalog.DefaultSystemConsts["ppt_r"];

            double height = 90d, width = 160d;
            double gridHeight = previewContainerGrid.ActualHeight;
            double gridWidth = previewContainerGrid.ActualWidth;
            var ratio = Math.Min(gridHeight / height, gridWidth / width);

            height *= ratio;
            width *= ratio;

            previewCanvas.Height = height;
            previewCanvas.Width = width;

            previewRectangle.Height = ppth * height;
            previewRectangle.Width = pptw * width;

            previewRectangleTranslate.X = pptx * width - pptw * width / 2;
            previewRectangleTranslate.Y = ppty * height - ppth * height / 2;

            previewRectangleRotation.Angle = pptr;
        }

        private void MainWindow_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            ResetCanvas();
        }

        private void textBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                ApplyAnimation();
            }
        }

        // 依照预算数据创建动画
        private AnimationTimeline CreateAnimationTimeLine(KeyPointInformation keyPoint1, KeyPointInformation keyPoint2, PPT.MsoAnimProperty msoAnimProperty, List<double> resultDataList, double speed = 5000)
        {
            var startTime = keyPoint1.Time;
            var endTime = keyPoint2.Time;

            var deltaTime = endTime - startTime;

            var anim = new DoubleAnimationUsingKeyFrames();
            var keyframes = new DoubleKeyFrameCollection();

            var interval = speed * deltaTime / resultDataList.Count;

            var discreteThreshold = 0.1; // 补帧差距上限

            for (int i = 0; i < resultDataList.Count; i++)
            {
                var timespan = TimeSpan.FromMilliseconds(interval * i);
                var keytime = KeyTime.FromTimeSpan(timespan);
                DoubleKeyFrame newkeyframe;

                if (i != 0 && Math.Abs(resultDataList[i] - resultDataList[i - 1]) > discreteThreshold)
                    newkeyframe = new DiscreteDoubleKeyFrame(resultDataList[i] * previewCanvas.Width - previewRectangle.Width / 2, keytime);
                else
                    newkeyframe = new LinearDoubleKeyFrame(resultDataList[i] * previewCanvas.Width - previewRectangle.Width / 2, keytime);

                keyframes.Add(newkeyframe);
            }
            anim.KeyFrames = keyframes;

            return anim;
        }

        private AnimationTimeline CreateAnimationTimeLine(IList<KeyPointInformation> keypointList, PPT.MsoAnimProperty msoAnimProperty, double speed = 5000, int innerCount = 50)
        {
            var datalistResult = mainEditModel.CalculateAllExpression(keypointList, innerCount);

            if (datalistResult.IsValid() == false) return null;

            var discreteThreshold = 0.1; // 补帧差距上限

            var anim = new DoubleAnimationUsingKeyFrames();
            var keyframes = new DoubleKeyFrameCollection();

            for (int i = 0; i < datalistResult.Results.Count; i++)
            {
                var resultDataList = datalistResult.Results[i].DataList;
                var curItem = keypointList[i];
                var nextItem = keypointList[i + 1];

                var deltaTime = nextItem.Time - curItem.Time;
                var beginSpeed = curItem.Time * speed;
                var partSpeed = deltaTime * speed; // 速度块
                var interval = partSpeed / resultDataList.Count;

                for (int j = 0; j < resultDataList.Count; j++)
                {
                    var tempTime = beginSpeed + interval * j;
                    var timespan = TimeSpan.FromMilliseconds(tempTime);
                    var keytime = KeyTime.FromTimeSpan(timespan);
                    DoubleKeyFrame newkeyframe;

                    var dependenceValue = 0d;
                    switch (msoAnimProperty)
                    {
                        case MsoAnimProperty.msoAnimX:
                            dependenceValue = resultDataList[j] * previewCanvas.Width - previewRectangle.Width / 2;
                            break;
                        case MsoAnimProperty.msoAnimY:
                            dependenceValue = resultDataList[j] * previewCanvas.Height - previewRectangle.Height / 2;
                            break;
                        case MsoAnimProperty.msoAnimWidth:
                            dependenceValue = resultDataList[j] * previewCanvas.Width;
                            break;
                        case MsoAnimProperty.msoAnimHeight:
                            dependenceValue = resultDataList[j] * previewCanvas.Height;
                            break;
                        case MsoAnimProperty.msoAnimOpacity:
                            dependenceValue = resultDataList[j];
                            break;
                        case MsoAnimProperty.msoAnimRotation:
                            dependenceValue = resultDataList[j];
                            break;
                        default:
                            break;
                    }

                    if (j != 0 && Math.Abs(resultDataList[j] - resultDataList[j - 1]) > discreteThreshold)
                        newkeyframe = new DiscreteDoubleKeyFrame(dependenceValue, keytime);
                    else
                        newkeyframe = new LinearDoubleKeyFrame(dependenceValue, keytime);

                    keyframes.Add(newkeyframe);
                }
            }


            anim.KeyFrames = keyframes;

            return anim;
        }

        /// <summary>
        /// 应用动画。
        /// </summary>
        private void ApplyAnimation()
        {
            var anim = CreateAnimationTimeLine(mainEditModel.KeyPointList, mainEditModel.AnimProperty);

            switch (mainEditModel.AnimProperty)
            {
                case PPT.MsoAnimProperty.msoAnimNone:
                    break;
                case PPT.MsoAnimProperty.msoAnimX:
                    previewRectangleTranslate.BeginAnimation(TranslateTransform.XProperty, anim);
                    break;
                case PPT.MsoAnimProperty.msoAnimY:
                    previewRectangleTranslate.BeginAnimation(TranslateTransform.YProperty, anim);
                    break;
                case PPT.MsoAnimProperty.msoAnimWidth:
                    previewRectangle.BeginAnimation(Rectangle.WidthProperty, anim);
                    break;
                case PPT.MsoAnimProperty.msoAnimHeight:
                    previewRectangle.BeginAnimation(Rectangle.HeightProperty, anim);
                    break;
                case PPT.MsoAnimProperty.msoAnimOpacity:
                    previewRectangle.BeginAnimation(Rectangle.OpacityProperty, anim);
                    break;
                case PPT.MsoAnimProperty.msoAnimRotation:
                    previewRectangleRotation.BeginAnimation(RotateTransform.AngleProperty, anim);
                    break;
                default:
                    previewRectangleTranslate.BeginAnimation(TranslateTransform.XProperty, anim);
                    break;
            }
            //switch(mainEditModel.AnimProperty)
        }

        private void 播放_Click(object sender, RoutedEventArgs e)
        {
            ApplyAnimation();

        }

        private void 刷新_Click(object sender, RoutedEventArgs e)
        {
            ResetCanvas();

        }

        private void 确认_Click(object sender, RoutedEventArgs e)
        {
            Save(_pptPoints);

        }

        private void resultFormulaDataList_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (mainEditModel.IsNotLastItem == false) return;
            var savedKeypoint = (sender as ListBox).SelectedItem as SavedKeyPointInformation;
            if (savedKeypoint == null) return;
            mainEditModel.CurrentFormula += "   +   " + savedKeypoint.Formula;
            mainEditModel.StartValue = savedKeypoint.FromValue;
            mainEditModel.EndValue = savedKeypoint.ToValue;
        }

        private void keypointList_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            var keypoint = (sender as ListBox).SelectedItem as KeyPointInformation;
            if (keypoint == null) return;
            mainEditModel.SelectedItem = keypoint;
        }

        private void addKeyPointButton_Click(object sender, RoutedEventArgs e)
        {
            mainEditModel.AddAfterItem(mainEditModel.SelectedItem);
            keyPointListBox.SelectedItem = mainEditModel.SelectedItem;
        }

        private void deleteKeyPointButton_Click(object sender, RoutedEventArgs e)
        {
            // 如果只存在两个关键帧
            if (mainEditModel.HasItem)
            {
                mainEditModel.RemoveItem(mainEditModel.SelectedItem);
                keyPointListBox.SelectedItem = mainEditModel.SelectedItem;
            }
         
        }

        private void startTimeTextBlock_IncreaseAbsValue(object sender, RoutedEventArgs e)
        {
            var tb = sender as An_Im.Theme.Custom.AnAdjustableTextBox;
            var datacontext = tb.DataContext as MainEditModel;
            
            var change = tb.LargeChange;
            if (Keyboard.GetKeyStates(Key.LeftCtrl) == KeyStates.Down)
                change = tb.SmallChange;

            if (mainEditModel.HasItem)
            {
                datacontext.StartTime = Math.Min(1, datacontext.StartTime + change);
            }
        }

        private void startTimeTextBlock_DecreaseAbsValue(object sender, RoutedEventArgs e)
        {
            var tb = sender as An_Im.Theme.Custom.AnAdjustableTextBox;
            var datacontext = tb.DataContext as MainEditModel;

            var change = tb.LargeChange;
            if (Keyboard.GetKeyStates(Key.LeftCtrl) == KeyStates.Down)
                change = tb.SmallChange;

            if (mainEditModel.HasItem)
            {
                datacontext.StartTime = Math.Max(0, datacontext.StartTime - change);
            }
        }

        private void endTimeTextBlock_IncreaseAbsValue(object sender, RoutedEventArgs e)
        {
            var tb = sender as An_Im.Theme.Custom.AnAdjustableTextBox;
            var datacontext = tb.DataContext as MainEditModel;

            var change = tb.LargeChange;
            if (Keyboard.GetKeyStates(Key.LeftCtrl) == KeyStates.Down)
                change = tb.SmallChange;

            if (mainEditModel.IsNotLastItem)
            {
                datacontext.EndTime = Math.Min(1, datacontext.EndTime + change);
            }
        }

        private void endTimeTextBlock_DecreaseAbsValue(object sender, RoutedEventArgs e)
        {
            var tb = sender as An_Im.Theme.Custom.AnAdjustableTextBox;
            var datacontext = tb.DataContext as MainEditModel;

            var change = tb.LargeChange;
            if (Keyboard.GetKeyStates(Key.LeftCtrl) == KeyStates.Down)
                change = tb.SmallChange;

            if (mainEditModel.IsNotLastItem)
            {
                datacontext.EndTime = Math.Max(0, datacontext.EndTime - change);
            }
        }
    }
}
